import React, { useEffect, useRef, useState, useCallback } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; // Added .js to the import path
import { initializeApp } from 'firebase/app';
import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from 'firebase/auth';
import { getFirestore, doc, setDoc, getDoc, onSnapshot } from 'firebase/firestore';
// Componente principal de la aplicación
function App() {
const [activeModule, setActiveModule] = useState('overview');
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const [userId, setUserId] = useState(null);
const [isAuthReady, setIsAuthReady] = useState(false);
const [db, setDb] = useState(null);
const [auth, setAuth] = useState(null);
const [userProgress, setUserProgress] = useState({});
const [showModal, setShowModal] = useState(false);
const [modalMessage, setModalMessage] = useState('');
// Configuración de Firebase - ¡No modificar, se inyecta en tiempo de ejecución!
const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {};
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id';
const initialAuthToken = typeof __initial_auth_token !== 'undefined' ? __initial_auth_token : null;
// Inicialización de Firebase y autenticación
useEffect(() => {
try {
const app = initializeApp(firebaseConfig);
const firestoreDb = getFirestore(app);
const firebaseAuth = getAuth(app);
setDb(firestoreDb);
setAuth(firebaseAuth);
const unsubscribe = onAuthStateChanged(firebaseAuth, async (user) => {
if (user) {
setUserId(user.uid);
setIsAuthReady(true);
} else {
// Si no hay token inicial, iniciar sesión anónimamente
if (!initialAuthToken) {
await signInAnonymously(firebaseAuth);
}
// El onAuthStateChanged se disparará de nuevo con el usuario anónimo
}
});
// Si hay un token inicial, usarlo para iniciar sesión
if (initialAuthToken) {
signInWithCustomToken(firebaseAuth, initialAuthToken)
.catch((error) => {
console.error("Error signing in with custom token:", error);
// Fallback a inicio de sesión anónimo si falla el token personalizado
signInAnonymously(firebaseAuth).catch(console.error);
});
}
return () => unsubscribe();
} catch (error) {
console.error("Error initializing Firebase:", error);
setModalMessage("Error al inicializar Firebase. Algunas funcionalidades podrían no estar disponibles.");
setShowModal(true);
}
}, [initialAuthToken, firebaseConfig]);
// Cargar el progreso del usuario desde Firestore
useEffect(() => {
if (db && userId && isAuthReady) {
const userDocRef = doc(db, `artifacts/${appId}/users/${userId}/digestive_progress`, 'progress');
const unsubscribe = onSnapshot(userDocRef, (docSnap) => {
if (docSnap.exists()) {
setUserProgress(docSnap.data());
} else {
setUserProgress({}); // No hay progreso aún
}
}, (error) => {
console.error("Error fetching user progress:", error);
setModalMessage("Error al cargar el progreso del usuario.");
setShowModal(true);
});
return () => unsubscribe();
}
}, [db, userId, isAuthReady, appId]);
// Guardar el progreso del usuario en Firestore
const saveUserProgress = useCallback(async (newProgress) => {
if (db && userId && isAuthReady) {
try {
const userDocRef = doc(db, `artifacts/${appId}/users/${userId}/digestive_progress`, 'progress');
await setDoc(userDocRef, newProgress, { merge: true });
// Eliminado el mensaje de éxito del modal aquí
} catch (error) {
console.error("Error saving user progress:", error);
setModalMessage("Error al guardar el progreso del usuario.");
setShowModal(true);
}
} else {
setModalMessage("No se pudo guardar el progreso. Autenticación no lista.");
setShowModal(true);
}
}, [db, userId, isAuthReady, appId]);
const handleModuleClick = (moduleName) => {
setActiveModule(moduleName);
setIsSidebarOpen(false); // Cerrar sidebar al seleccionar módulo
// Ejemplo de cómo podrías guardar el progreso al cambiar de módulo
saveUserProgress({ lastModuleVisited: moduleName });
};
const closeModal = () => setShowModal(false);
return (
// La estructura de la aplicación está diseñada para ser autocontenida y embeddable.
// Puede ser montada en cualquier elemento DOM y se adaptará a su contenedor.
{/* Modal de Mensajes */}
{showModal && (
{modalMessage}
)}
{/* Encabezado */}
Anatomía Digestiva Interactiva
{userId && (
ID de Usuario: {userId}
)}
{/* Contenido Principal */}
{/* Barra Lateral (Sidebar) */}
{/* Área de Contenido Principal */}
{activeModule === 'overview' && (
Visión General del Sistema Digestivo
Explora el fascinante viaje de los alimentos a través de tu cuerpo.
El sistema digestivo es un conjunto de órganos que trabajan en conjunto para transformar los alimentos que consumes en nutrientes que tu cuerpo puede usar para energía, crecimiento y reparación.
Este módulo te ofrece un mapa interactivo del trayecto digestivo, destacando los principales órganos y sus funciones.
{/* Aquí iría el mapa esquemático interactivo */}
[Mapa esquemático interactivo del trayecto digestivo con puntos interactivos]
)}
{activeModule === 'macroscopic' && (
Órganos Macroscópicos
Sumérgete en la anatomía visible de cada componente del sistema digestivo.
Desde la boca hasta el ano, cada órgano tiene una estructura única que le permite cumplir su función específica en la digestión.
Esófago: Tubo muscular que conecta la faringe con el estómago. Capas de la pared (mucosa, submucosa, muscular, adventicia/serosa).
Estómago: Órgano en forma de J. Regiones anatómicas (cardias, fundus, cuerpo, antro pilórico, píloro). Curvaturas mayor y menor.
Intestino Delgado: Tubo largo y enrollado.
Duodeno: Primera sección, en forma de C.
Yeyuno: Sección media, principal lugar de absorción.
Íleon: Última sección, se une al intestino grueso.
Características: Pliegues circulares (válvulas de Kerckring), vellosidades y microvellosidades.
Intestino Grueso:
Ciego: Bolsa inicial con el apéndice vermiforme.
Colon: Ascendente, transverso, descendente, sigmoides. Haustras y tenias cólicas.
Recto: Almacena heces antes de la defecación.
Ano: Abertura terminal con esfínteres interno y externo.
Órganos Accesorios:
Hígado: Glándula más grande del cuerpo. Lobulillos hepáticos, vías biliares.
Vesícula Biliar: Almacena y concentra la bilis.
Páncreas: Glándula exocrina (enzimas digestivas) y endocrina (hormonas). Acinos pancreáticos y conductos.
{/* Aquí irían modelos 3D interactivos de órganos individuales */}
[Modelos 3D interactivos de órganos individuales con hotspots y fichas informativas]
)}
{activeModule === 'microanatomy' && (
Microanatomía e Histología
Explora el mundo microscópico de los tejidos digestivos.
La función del sistema digestivo se basa en la intrincada estructura de sus células y tejidos, invisibles a simple vista.
Vellosidades y Microvellosidades: Propósito y estructura.
Criptas de Lieberkühn, Glándulas Gástricas y Acinos Pancreáticos.
{/* Aquí irían cortes histológicos interactivos con zoom */}
[Cortes histológicos interactivos con zoom, leyendas y herramienta de comparación]
)}
{activeModule === 'vascularization' && (
Vascularización y Drenaje Linfático
Comprende cómo los nutrientes son transportados y cómo el sistema inmune protege el tracto digestivo.
Redes Arteriales y Venosas: Diagramas interactivos.
Plexos Nerviosos y Ganglios Linfáticos Asociados.
{/* Aquí irían infografías interactivas */}
[Infografías interactivas de rutas vasculares y linfáticas]
)}
{activeModule === '3d-integration' && (
Integración 3D y Capas Anatómicas
Manipula un modelo 3D completo del sistema digestivo, aislando órganos y explorando sus capas.
Usa el ratón para rotar el modelo. Haz clic y arrastra para mover la cámara. Usa la rueda del ratón para hacer zoom.
{/* Controles para el modelo 3D (ej. selección de capas, cortes) */}
Progreso actual del usuario: {JSON.stringify(userProgress)}
)}
{/* Pie de página */}
);
}
// Componente para el renderizado 3D con Three.js
function DigestiveSystem3D() {
const mountRef = useRef(null);
useEffect(() => {
const currentMount = mountRef.current;
if (!currentMount) return;
// Configuración de la escena
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0); // Color de fondo claro
// Configuración de la cámara
const camera = new THREE.PerspectiveCamera(75, currentMount.clientWidth / currentMount.clientHeight, 0.1, 1000);
camera.position.z = 5;
// Configuración del renderizador
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(currentMount.clientWidth, currentMount.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
currentMount.appendChild(renderer.domElement);
// Añadir controles de órbita para interactividad
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // para un movimiento más suave
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 2;
controls.maxDistance = 10;
// Añadir un modelo 3D de ejemplo (un cubo para representar un órgano)
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshPhongMaterial({ color: 0x007bff }); // Material con luz
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Añadir luces para que el modelo sea visible
const ambientLight = new THREE.AmbientLight(0x404040); // Luz ambiental suave
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); // Luz direccional
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
// Función de animación
const animate = () => {
requestAnimationFrame(animate);
controls.update(); // solo si enableDamping está activado
renderer.render(scene, camera);
};
animate();
// Manejar el redimensionamiento de la ventana
const handleResize = () => {
camera.aspect = currentMount.clientWidth / currentMount.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(currentMount.clientWidth, currentMount.clientHeight);
};
window.addEventListener('resize', handleResize);
// Limpieza al desmontar el componente
return () => {
window.removeEventListener('resize', handleResize);
if (currentMount) {
currentMount.removeChild(renderer.domElement);
}
renderer.dispose();
geometry.dispose();
material.dispose();
controls.dispose(); // Disponer de los controles de órbita
// Limpiar la escena si es necesario, aunque Three.js suele manejarlo
};
}, []); // El array de dependencias vacío asegura que se ejecute una sola vez
return ;
}
export default App;